﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;  //usado para administrar los hilos
using System.Collections;  //usado para ArrayList
using SistemaCenterQuimPost.Entidad;  //Usado para instanciar objetos de clase Producto y agilizar el filtrado de descripción.
using System.Text.RegularExpressions; //usado para expresiones regulares. Filtrar descripciones
using SistemaCenterQuimPost.Control; //usado para validar datos.
using SistemaCenterQuimPost.Plantillas; //necesario para obtener fecha, nombre usuario etc,

namespace SistemaCenterQuimPost.UI
{
    public partial class frmGestionProductos : SistemaCenterQuimPost.Plantillas.frmPlantillaHijo
    {

        //-------------------ATRIBUTOS--------------------------------------------------------

        //(ALE)COLOCAR BIEN ESTAS 2 INSTRUCCIONES SI ES QUE NO LO ESTÁN. 
        protected DataSet2 centerQuimDs;  //Atributos usados para obtener datos de la base de datos.   
        
        protected DataSet2TableAdapters.ProductosTableAdapter productosTa;
        protected DataSet2TableAdapters.MovimientosxProductoTableAdapter movimientosTa;

        protected bool refrescarResgProductos; //La primera vez debe refrescar el resgProductos.

        protected ArrayList resgProductos; // colección usada para guardar Codigos de productos y descripción, se tiene una pequeña base de dato para realizar las busqueda mediante filtros más rapidamente.

        protected string[] resgProductoActual; //al ser una interface polifuncional. Guarda el producto encontrado en una busqueda exitosa. {codigoBarra, descripcion, precioCosto, precioVenta, stockActual, cantMinima}   

        protected Thread oHiloActualizarResgProductos; //hilos que son ejecutados concurrentemente con la aplicación.
        protected Thread oHiloFiltrarDescripcion;

        protected string estadoDelFormulario;

        //------------------CONSTRUCTORES---------------------------------------------------------------------

        public frmGestionProductos()  //este constructor es llamado desde frmGestionProductosA
        {
            InitializeComponent();

            estadoDelFormulario = "";

            refrescarResgProductos = true; //inicializamos a true, ya que la primera vez que trate de usar ResgProductos tendra que actualizar.

            //(ALE) COLOCAR BIEN ESTAS 2 INSTRUCCIONES SI ES QUE NO LO ESTÁN
            this.centerQuimDs = new DataSet2();

            this.productosTa = new DataSet2TableAdapters.ProductosTableAdapter();
            this.movimientosTa = new DataSet2TableAdapters.MovimientosxProductoTableAdapter();

            this.resgProductos = new ArrayList();  //se inicializa la colección ArrayList que guardará una base de datos parcial.          
            this.resgProductoActual = new String[6];  //se inicializa el array de string que almacena el producto actual.

            this.oHiloActualizarResgProductos = new Thread(new ThreadStart(actualizarResgProductos)); //Instancia el hilo y lo delega al metodo actualizarResgProductos. 
            this.oHiloFiltrarDescripcion = new Thread(new ThreadStart(filtrarDescripcion));


        }

        public frmGestionProductos(int ancho, int alto) //frmGestiónProductos usan Administrador y Cajero, ambos usan diferentes 
        //tamaño de ventana. Al usar este formulario se reciben los tamaños de ventana ha usar en cada uno.
        {
            InitializeComponent();

            estadoDelFormulario = "";

            refrescarResgProductos = true; //inicializamos a true, ya que la primera vez que trate de usar ResgProductos tendra que actualizar.

            this.Width = ancho;
            this.Height = alto;
            this.MinimumSize = new System.Drawing.Size(ancho, alto); //Se establece el tamaño máximo y mínimo en pixeles.
            this.MaximumSize = new System.Drawing.Size(ancho, alto);

            //(ALE) COLOCAR BIEN ESTAS 3 INSTRUCCIONES SI ES QUE NO LO ESTÁN
            this.centerQuimDs = new DataSet2();
            this.productosTa = new DataSet2TableAdapters.ProductosTableAdapter();
            this.movimientosTa = new DataSet2TableAdapters.MovimientosxProductoTableAdapter();


            this.resgProductos = new ArrayList();  //se inicializa la colección ArrayList que guardará una base de datos parcial.          
            this.resgProductoActual = new String[6];  //se inicializa el array de string que almacena el producto actual.

            this.oHiloActualizarResgProductos = new Thread(new ThreadStart(actualizarResgProductos)); //Instancia el hilo y lo delega al metodo actualizarResgProductos. 
            this.oHiloFiltrarDescripcion = new Thread(new ThreadStart(filtrarDescripcion));

        }


        //-----------------ESTADOS-------------------------------------------------------------

        protected virtual void frmGestionProductos_Load(object sender, EventArgs e)
        {
            CheckForIllegalCrossThreadCalls = false;  //necesario para trabajar con hilos.

            resguardarProductoActual(); //guarda el estado inicial del formulario.

            estEsperandoOperacion(); //estado al comenzar el formulario o al fracasar en la busqueda.

        }

        //virtual me permite redefinir, en este caso solo en las clases hijas
        protected virtual void estEsperandoOperacion() //estado luego de realizar una busqueda exitosa, agregar o editar. Es decir con datos en vista.
        {
            txtPrecioCosto.UseSystemPasswordChar = true; //Se coloca como pasword el precio de costo para que no vea el cajero.
            txtPrecioCosto.PasswordChar = '*';

            if (this.txtPrecioVenta.Text == "") //estado al iniciar el formulario
            {
                System.Windows.Forms.Control[] controlesHabilitados = { this.txtCodBarra, this.btnBusCodigo, this.txtDescripcion, this.btnBusDescripcion, this.btnAgregar };
                System.Windows.Forms.Control[] controlesDeshabilitados = { this.lstProductosCodBarra, this.lstProductosDescripcion, this.txtPrecioCosto, this.txtPrecioVenta, this.txtStockActual, this.txtCantMinima, this.btnEditar, this.btnProcesar, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin };
                System.Windows.Forms.Control[] controlesVisibles = {};
                System.Windows.Forms.Control[] controlesInvisibles = { this.lstProductosCodBarra, this.lstProductosDescripcion, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin, lblError };

                habilitarControles(controlesHabilitados);
                deshabilitarControles(controlesDeshabilitados);
                visibleControles(controlesVisibles);
                invisibleControles(controlesInvisibles);

                if (refrescarResgProductos) //si hace falta actualizar el refrescarResgProducto se realiza
                {
                    ejecutarHiloActualizarResgProductos();

                }

            }
            else   //estado luego de una busqueda.
            {
                System.Windows.Forms.Control[] controlesHabilitados = { this.txtCodBarra, this.btnBusCodigo, txtDescripcion, btnBusDescripcion, btnAgregar, btnEditar};
                System.Windows.Forms.Control[] controlesDeshabilitados = { this.lstProductosCodBarra, this.lstProductosDescripcion, this.txtPrecioCosto, this.txtPrecioVenta, this.txtStockActual, this.txtCantMinima, btnProcesar, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin };
                System.Windows.Forms.Control[] controlesVisibles = { };
                System.Windows.Forms.Control[] controlesInvisibles = { this.lstProductosCodBarra, this.lstProductosDescripcion, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin, lblError };

                habilitarControles(controlesHabilitados);
                deshabilitarControles(controlesDeshabilitados);
                visibleControles(controlesVisibles);
                invisibleControles(controlesInvisibles);

            }

            errorProvider1.Clear();  //se borran los errores, si habian.

            txtCodBarra.Focus();   //se deja el foco en txtCodBarra

            estadoDelFormulario = "estEsperandoOperacion";
        }

        protected virtual void estAgregar()
        {
            txtPrecioCosto.UseSystemPasswordChar = false; //Se coloca el pasword a falso. En el estado agregar puede ver el costo del producto.
            txtPrecioCosto.PasswordChar = '\0';
            
            System.Windows.Forms.Control[] controlesHabilitados = { this.txtCodBarra, this.txtDescripcion, this.txtPrecioCosto, this.txtPrecioVenta, this.txtStockActual, this.txtCantMinima, btnAgregar, btnProcesar, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin };
            System.Windows.Forms.Control[] controlesDeshabilitados = { this.lstProductosCodBarra, this.btnBusCodigo, this.lstProductosDescripcion, btnBusDescripcion, btnEditar };
            System.Windows.Forms.Control[] controlesVisibles = {lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblErrorCantMin };
            System.Windows.Forms.Control[] controlesInvisibles = { this.lstProductosCodBarra, this.lstProductosDescripcion, lblError };

            habilitarControles(controlesHabilitados);
            deshabilitarControles(controlesDeshabilitados);
            visibleControles(controlesVisibles);
            invisibleControles(controlesInvisibles);

            errorProvider1.Clear();

            txtCodBarra.Focus();

            estadoDelFormulario = "estAgregar";
        }

        protected virtual void estEditar()
        {
            txtPrecioCosto.UseSystemPasswordChar = true; //Se coloca como pasword el precio de costo para que no vea el cajero.
            txtPrecioCosto.PasswordChar = '*';
            
            System.Windows.Forms.Control[] controlesHabilitados = { this.txtCantMinima, this.btnEditar, this.btnProcesar, lblErrorCantMin };
            System.Windows.Forms.Control[] controlesDeshabilitados = { this.txtCodBarra, this.btnBusCodigo, this.lstProductosCodBarra, this.txtDescripcion, lstProductosDescripcion, this.btnBusDescripcion, this.txtPrecioCosto, this.txtPrecioVenta, this.txtStockActual, this.btnAgregar, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual };
            System.Windows.Forms.Control[] controlesVisibles = {lblErrorCantMin };
            System.Windows.Forms.Control[] controlesInvisibles = { this.lstProductosCodBarra, lstProductosDescripcion, lblErrorDescripcion, lblErrorPCosto, lblErrorPVenta, lblErrorStockActual, lblError };

            habilitarControles(controlesHabilitados);
            deshabilitarControles(controlesDeshabilitados);
            visibleControles(controlesVisibles);
            invisibleControles(controlesInvisibles);

            errorProvider1.Clear();

            txtCodBarra.Focus();

            estadoDelFormulario = "estEditar";
        }

        //--------------------METODOS VARIOS--------------------------------------------------------------------------------

        protected virtual void vaciarTextBox() //Se deja en blanco todos los textBox, usado en estados estInicial y estAgregar.
        {
            txtCodBarra.Text = "";
            txtDescripcion.Text = "";
            txtPrecioCosto.Text = "";
            txtPrecioVenta.Text = "";
            txtStockActual.Text = "";
            txtCantMinima.Text = "";
        }

        //Virtual, permite redefinir en frmAltaProductosDisueltos y frmRecibirProductos
        protected virtual void actualizarResgProductos() //Se refresca el arrayList resgProductos
        {
            try
            {
                //(ALE) HACER EL FILL O COMO LO LLAMES ACA ABAJO
                //ProductosTa.FillByProductos1(centerQuimDs,...);
                productosTa.Fill(centerQuimDs.Productos);

                resgProductos.Clear(); //Se vacia resgProductos (ArrayList)

                foreach (DataRow unaFila in centerQuimDs.Productos) //Se recorre todas las filas  del centerQuimDs.Productos y se cargan los productos al ArrayList
                {
                    resgProductos.Add(new Producto(Convert.ToString(unaFila[0]), Convert.ToString(unaFila[1])));
                }

                refrescarResgProductos = false;

            }
            catch (IndexOutOfRangeException)
            {
                //si llega a ocurrir esta excepcion no se tiene en cuenta. Podría no haber productos cargados y lanzaría esta excepción.

            }
            catch (Exception ex)
            {
                //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                //Si llega a ocurrir algún error grave se cierra el formulario 
                this.Close();
            }

        }

        protected void btnBusCodigo_Click(object sender, EventArgs e)  //método que se ejecuta cuando se presiona sobre el botón btnBusCodigo
        {
            eventoBusCodigo();

        }

        protected virtual void eventoBusCodigo()  //Se encapsula en un método para que pueda usar cuando se presiona Enter sobre el txtCodBarra en estado de busqueda o click en btnBusCodBarra.
        {

            //no hace falta que pregunte si txtCodBarra es blanco, ya que por la forma de filtrar no encontrará nada igual.                                

            if (refrescarResgProductos)   //si es necesario actualizar el resgProductos se lo hace sino no
            {
                ejecutarHiloActualizarResgProductos();  //ejecuta el hilo que actualiza el ResgProductos
            }

            filtrarCodigoBarra();


            if (lstProductosCodBarra.Items.Count == 0)  //el filtro no obtuvo coincidencias.
            {
                //Se informa que no se encontró nada 
                MessageBox.Show("Su consulta no ha arrojado resultados.", "INFORME", MessageBoxButtons.OK, MessageBoxIcon.Information);

                deshacerCambios(); //coloca el estado de la ultima busqueda exitosa de un producto.

                //Como no encontró nada se deja en este estado.
                this.estEsperandoOperacion();
            }
            else
            {
                if (lstProductosCodBarra.Items.Count == 1)   //solo se encontró una sola coincidencia. No es necesario mostrar el lstProductosCodBarra. Directamente se muestra el producto.
                {
                    this.estadoDelFormulario = "Buscando";  //Se coloca este estado ya que si encuentra algo, cargará el txtDescripcion y lanzará el evento TextChanged()

                    cargarProductoPorCodigo(this.txtCodBarra.Text.Trim());  //busca producto con codigo de barra único (una coincidencia) encontrado en el oHiloFiltrarCodBarra y lo carga en los textBox.

                    estEsperandoOperacion(); //se deja en el estado necesario

                    resguardarProductoActual(); //Guarda los valores de los textBox luego de una busqueda exitosa.
                }
                else
                {
                    //si se obtuvo más de una coincidencia, solo se muestran éstas en lstProductosCodBarra, y espera a que el usuario seleccione una.

                    lstProductosCodBarra.Visible = true; 
                    lstProductosCodBarra.Enabled = true;

                    //lstProductosCodBarra.Focus(); //se deja el foco en el dgvProductosCodBarra para que elija alguna coincidencia.

                    //lstProductosCodBarra.SelectedIndex=0;  //se deja seleccionado el primer items.
                }

            }

        }

        protected void ejecutarHiloActualizarResgProductos() // hilo para actualizar el atributo ResgProductos
        {
            if ((oHiloActualizarResgProductos.ThreadState == ThreadState.Unstarted) || (oHiloActualizarResgProductos.ThreadState == ThreadState.Stopped))
            {
                this.oHiloActualizarResgProductos = new Thread(new ThreadStart(actualizarResgProductos));
                this.oHiloActualizarResgProductos.Priority = ThreadPriority.Highest;
                this.oHiloActualizarResgProductos.Start();  //Se ejecuta el hilo para actualizar el atributo ResgProductos
            }
        }



        protected virtual void resguardarProductoActual() //Guarda los valores de los textBox en array resgProductoActual, luego de una busqueda exitosa.
        {
            resgProductoActual[0] = txtCodBarra.Text;
            resgProductoActual[1] = txtDescripcion.Text;
            resgProductoActual[2] = txtPrecioCosto.Text;
            resgProductoActual[3] = txtPrecioVenta.Text;
            resgProductoActual[4] = txtStockActual.Text;
            resgProductoActual[5] = txtCantMinima.Text;

        }

        protected virtual void deshacerCambios()  //usado cuando en estado editar o agregar, se arrepiente (no se apreta en procesar) y se vuelve a estado esperandoOperacion.
        {
            txtCodBarra.Text = resgProductoActual[0];
            txtDescripcion.Text = resgProductoActual[1];
            txtPrecioCosto.Text = resgProductoActual[2];
            txtPrecioVenta.Text = resgProductoActual[3];
            txtStockActual.Text = resgProductoActual[4];
            txtCantMinima.Text = resgProductoActual[5];

        }

        
        protected virtual void cargarProductoPorCodigo(string unCodBarra) //busca producto con codigo de barra único (una coincidencia) encontrado en el oHiloFiltrarCodBarra y lo carga en los textBox.
        {
            try
            {
                //(ALE)HACER EL FILL O COMO LO LLAMES ACA ABAJO
                //ProductosTa.FillByProductos1(centerQuimDs,unCodBarra);
                productosTa.FillByCodigo(centerQuimDs.Productos, unCodBarra);

                //ARREGLAR ESTO. Se rellena los textBox con la consulta encontrada. 
                txtCodBarra.Text = centerQuimDs.Productos[0].codProducto;
                txtDescripcion.Text = centerQuimDs.Productos[0].descripcion;
                txtPrecioCosto.Text = centerQuimDs.Productos[0].precioCosto.ToString();
                txtPrecioVenta.Text = centerQuimDs.Productos[0].precioVenta.ToString();
                txtCantMinima.Text = centerQuimDs.Productos[0].cantidadMinima.ToString();
                txtStockActual.Text = centerQuimDs.Productos[0].stockActual.ToString();
                
                                //...
                //..FALTAN LOS OTROS. ACORDATE QUE ESTE ES EL DE CAJERO, TIENE POCOS CONTROLES.

            }
            catch (IndexOutOfRangeException)
            {

            }
            catch (Exception ex)
            {
                //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                //Si llega a ocurrir algún error grave se cierra el formulario 
                this.Close();
            }
        }

        protected void filtrarCodigoBarra()  //Filtra y carga las descripciones (si las hay) en el lstProductos. Método que se ejecuta dentro de un hilo. 
        {

            this.oHiloActualizarResgProductos.Join(); //espera a que primero termine el oHiloActualizarResgProductos para proceder con el actual.

            lstProductosCodBarra.Items.Clear(); //vaciamos el lstProductosCodBarra por si tuviera descripciones anteriores.


            foreach (Producto unProducto in resgProductos) //buscamos las descripciones asociadas al código y la cargamos en el lstProductosCodBarra
            {
                if (this.txtCodBarra.Text.Trim() == unProducto.CodBarra)
                {
                    lstProductosCodBarra.Items.Add(unProducto.Descripcion);
                }

            }

        }

        protected void lstProductosCodBarra_DoubleClick(object sender, EventArgs e)  //captura en evento doble click sobre lstProductosCodBarra
        {
            procesarEleccionLstProductosCodBarra();
        }

        protected void procesarEleccionLstProductosCodBarra()  //se encapsulo para que se pueda usar con DoubleClick y Enter sobre el lstProductosCodBarra
        {
            try
            {
                this.estadoDelFormulario = "Buscando";  //Se coloca este estado ya que si encuentra algo, cargará el txtDescripcion y lanzará el evento TextChanged()

                cargarProductoPorDescripcion(lstProductosCodBarra.SelectedItem.ToString()); //busca producto por descripción en la base de datos, y lo carga en los textBox.

                resguardarProductoActual(); //Guarda los valores de los textBox luego de una busqueda exitosa.

                estEsperandoOperacion(); //se deja en el estado necesario

            }
            catch (IndexOutOfRangeException)
            {

            }
            catch (Exception ex)
            {
                //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                //Si llega a ocurrir algún error grave se cierra el formulario 
                this.Close();
            }
        }

        protected virtual void cargarProductoPorDescripcion(string unaDescripcion)  //busca el producto por descripción en la base de datos y lo carga en los textbox.
        {
            //(ALE) HACER EL FILL O COMO LO LLAMES ACA ABAJO
            productosTa.FillByNombre(centerQuimDs.Productos, unaDescripcion);

            //ARREGLAR ESTO. Se rellena los textBox con la consulta encontrada. 
            txtCodBarra.Text = centerQuimDs.Productos[0].codProducto;
            txtDescripcion.Text = centerQuimDs.Productos[0].descripcion;
            txtPrecioCosto.Text = centerQuimDs.Productos[0].precioCosto.ToString();
            txtPrecioVenta.Text = centerQuimDs.Productos[0].precioVenta.ToString();
            txtCantMinima.Text = centerQuimDs.Productos[0].cantidadMinima.ToString();
            txtStockActual.Text = centerQuimDs.Productos[0].stockActual.ToString();
            //..
            //...
            //..FALTAN LOS OTROS. ACORDATE QUE ESTE ES EL DE CAJERO, TIENE POCOS CONTROLES.

        }

        protected void txtCodBarra_KeyDown(object sender, KeyEventArgs e)  //captura el evento KeyDown sobre el txtCodBarra.
        {
            if (e.KeyCode == Keys.Enter)
            {
                if (estadoDelFormulario == "estEsperandoOperacion") //estado esperando operación o estado inicial
                {
                    eventoBusCodigo();

                }

                else //estado agregar o editar
                {
                    procesar();
                }

            }
            else
            {
                if ((e.KeyCode == Keys.Down) & (lstProductosCodBarra.Items.Count > 0) & (estadoDelFormulario == "estEsperandoOperacion")) //Si se presiona para abajo y tiene a lstProductosDescripcion visible (es decir se encuentra esperando operacion y con items en la lista)
                {

                    lstProductosCodBarra.SetSelected(0, true); //se deja seleccionado el primer items
                    lstProductosCodBarra.Focus(); //Se deja el foco sobre este control para que seleccione un items.
                }
            }

        }

        protected void lstProductosCodBarra_Leave(object sender, EventArgs e) //Oculta automáticamente el lstProductosCodBarra cuando el control pierde el foco.
        {
            this.lstProductosCodBarra.Enabled = false;
            this.lstProductosCodBarra.Visible = false;
            this.lstProductosCodBarra.Items.Clear();
        }



        protected void txtDescripcion_TextChanged(object sender, EventArgs e) //evento que se desencadena cuando cambia el valor del TextBox
        {
            if (this.estadoDelFormulario == "estEsperandoOperacion") //el proceso es capturado solo si está en este estado.
            {
                eventoBusDescripcion();
            }

        }

        protected void eventoBusDescripcion()
        {
            if (txtDescripcion.Text != "") //Si txtDescripcion es distinto de blanco se realiza el procesamiento. Esto se realiza para no perder el tiempo en filtro innecesarios.
            {
                if (refrescarResgProductos)   //si es necesario actualizar el resgProductos se lo hace sino no
                {
                    ejecutarHiloActualizarResgProductos();
                }

                ejecutarHiloFiltrarDescripcion();

                if (lstProductosDescripcion.Items.Count == 0)  //el filtro no obtuvo coincidencias, no se muestra el lstProductosDescripcion
                {
                    lstProductosDescripcion.Visible = false;
                    lstProductosDescripcion.Enabled = false;

                }
                else
                {
                    lstProductosDescripcion.Visible = true; //si se obtuvo una o más coincidencia se muestran en lstProductosDescripcion, y espera a que el usuario seleccione una.
                    lstProductosDescripcion.Enabled = true;

                }

            }
            else  //Si txtDescripcion es blanco no se realiza el procesamiento
            {
                lstProductosDescripcion.Enabled = false;
                lstProductosDescripcion.Visible = false;
            }
        }

        protected void ejecutarHiloFiltrarDescripcion()
        {
            if ((this.oHiloFiltrarDescripcion.ThreadState == ThreadState.Unstarted) || (this.oHiloFiltrarDescripcion.ThreadState == ThreadState.Stopped))
            {
                this.oHiloFiltrarDescripcion = new Thread(new ThreadStart(filtrarDescripcion)); //Instancia el hilo y lo delega al metodo filtrarDescripcion. 
                this.oHiloFiltrarDescripcion.Priority = ThreadPriority.AboveNormal;
                this.oHiloFiltrarDescripcion.Start();  //hilo que filtra las descripciones asociadas al valor del txtDescripcion y lo carga en lstProductosDescripcion
            }

        }


        protected void filtrarDescripcion()
        {
            //this.oHiloActualizarResgProductos.Join(); //espera a que primero termine el oHiloActualizarResgProductos para proceder con el actual.

            lstProductosDescripcion.Items.Clear(); //vaciamos el lstProductosDescripcion por si tuviera descripciones anteriores.

            try
            {
                foreach (Producto unProducto in resgProductos) //buscamos las descripciones que coinciden con (al menos una palabra o letra + txtDescripcion.text + ninguna o muchas palabras o letras) y la cargamos en el lstProductosDescripcion
                {

                    if (Regex.Match(unProducto.Descripcion, txtDescripcion.Text.Trim().ToUpper()).Success)
                    {
                        lstProductosDescripcion.Items.Add(unProducto.Descripcion);
                    }

                }
            }
            catch (InvalidExpressionException)
            {

            }
            
            catch (Exception)
            {
                txtDescripcion.Text = "";

            }

        }

        protected void txtDescripcion_KeyDown(object sender, KeyEventArgs e) //Método que captura el evento de presión de una tecla sobre txtDescripcion
        {
            if ((e.KeyCode == Keys.Down) & (lstProductosDescripcion.Items.Count > 0) & (estadoDelFormulario == "estEsperandoOperacion")) //Si se presiona para abajo y tiene a lstProductosDescripcion visible (es decir se encuentra esperando operacion y con items en la lista)
            {

                lstProductosDescripcion.SetSelected(0, true); //se deja seleccionado el primer items
                lstProductosDescripcion.Focus(); //Se deja el foco sobre este control para que seleccione un items.
                
            }
            else
            {
                if ((e.KeyCode == Keys.Enter) & (this.estadoDelFormulario != "estEsperandoOperacion"))
                {
                    procesar();  //Si se presiona enter estando en estado Agregar o Editar
                }

            }

        }
        
        

        protected void lstProductosDescripcion_Leave(object sender, EventArgs e) //Oculta automáticamente el lstProductosDescripcion cuando el control pierde el foco.
        {
            this.lstProductosDescripcion.Enabled = false;
            this.lstProductosDescripcion.Visible = false;
        }

        protected void lstProductosDescripcion_DoubleClick(object sender, EventArgs e) //evento que captura la elección de un item en el lstProductosDescripcion
        {
            procesarEleccionLstProductosDescripcion();
        }

        
        protected void procesarEleccionLstProductosDescripcion()  //Se encapsula para que capture Double click y Enter sobre  lstProductosDescripcion
        {
            try
            {
                this.estadoDelFormulario = "Buscando";

                cargarProductoPorDescripcion(lstProductosDescripcion.SelectedItem.ToString()); //busca producto por descripción y lo carga en los textBox.

                resguardarProductoActual(); //Guarda los valores de los textBox luego de una busqueda exitosa.

                estEsperandoOperacion(); //se deja en el estado necesario

            }
            catch (IndexOutOfRangeException)
            {

            }
            catch (Exception ex)
            {
                //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                //Si llega a ocurrir algún error grave se cierra el formulario 
                this.Close();
            }
        }

        protected void btnAgregar_Click(object sender, EventArgs e) //captura el evento click sobre el boton agregar
        {
            if (estadoDelFormulario == "estEsperandoOperacion")
            {
                estAgregar();  //deja en estado agregar

                resguardarProductoActual(); //se resguarda el producto que se estaba mostrando, por si no registra cambios. Aunque sea blanco no importa.

                vaciarTextBox(); //vaciamos los txtBox

                
            }
            else
            {
                
                deshacerCambios();  //vuelve a cargar el producto que se mostraba antes en ultima busqeda exitosa.

                estEsperandoOperacion();                

            }
        }

        protected virtual void btnEditar_Click(object sender, EventArgs e)
        {
            if (edicionValida())  //verifica que el cajero esté tratando de editar un producto terminado
            {
                if (estadoDelFormulario == "estEsperandoOperacion")
                {
                    estEditar();  //deja en estado editar

                    deshacerCambios();  //vuelve a cargar el producto que se mostraba antes, en la última busqueda exitosa

                    txtDescripcion.Text = this.resgProductoActual[1].Substring(2, this.resgProductoActual[1].Length - 2); //muetra la descripcion pero sin el string inicial del tipo.

                    
                }
                else
                {
                    deshacerCambios();  //vuelve a cargar el producto que se mostraba antes.

                    estEsperandoOperacion();

                }
            }
            else
            {
                //Se informa al cajero que no tiene permiso para editar el producto.
                MessageBox.Show("No tiene permiso para editar el producto.\n\n", "INFORMACION", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }

        }

        private bool edicionValida() //valida si el cajero efectivamente se está tratando de editar un producto terminado
        {
            if (resgProductoActual[1].Substring(0, 1) == "T")
            {
                return true;

            }
            else
            {
                return false;
            }
        }

        protected void btnProcesar_Click(object sender, EventArgs e)
        {
            procesar();
        }

        //Permite redefinir el comportamiento de este boton en frmAltaProductosDisueltos
        protected virtual void procesar() //se encapsulo en un método para usar cuando se presiona Enter con los textBox
        {
            if (estadoDelFormulario == "estAgregar") //estado agregar
            {
                if (formularioValido())
                {
                    try
                    {
                        agregarProducto();

                    }
                    catch (IndexOutOfRangeException)
                    {

                    }
                    catch (Exception ex)
                    {
                        //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                        MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                        //Si llega a ocurrir algún error en estado agregar 
                        this.Close();
                    }

                }
                else
                {
                    informarExistenciaError();
                }

            }
            else  //estado editar
            {
                if (formularioValido())
                {
                    try
                    {
                        editarProducto();

                    }
                    catch (IndexOutOfRangeException)
                    {

                    }
                    catch (Exception ex)
                    {
                        //Por cualquier otra excepcion se informa el error, en este caso es un error grave.
                        MessageBox.Show("La conexión a la base de datos no fué exitosa.\n\n Comuniquese con su desarrollador para solucionar el problema.\n\n" + ex.Message, "ERROR GRAVE - ACCESO A BASE DE DATO", MessageBoxButtons.OK, MessageBoxIcon.Error);

                        //Si llega a ocurrir algún error en estado agregar 
                        this.Close();
                    }

                }
                else
                {
                    informarExistenciaError();
                }
            }
        }

        protected virtual void editarProducto()
        {
            DialogResult result = MessageBox.Show("¿Desea actualizar el siguiente producto? \nCodigo: " + resgProductoActual[0] + ", - Descripción: " + resgProductoActual[1], "CONFIRMACION EDICION DE PRODUCTO", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

            if (result == DialogResult.Yes)
            {

                //FIJATE SI ESTÁ TODO BIEN. QUE COINCIDA CON TU CONSULTA
                productosTa.FillByNombre(centerQuimDs.Productos, resgProductoActual[1]);  //el argumento corresponde con la descripcion actual del producto a editar.
                
                centerQuimDs.Productos[0].descripcion = "T " + txtDescripcion.Text.Trim().ToUpper();
                centerQuimDs.Productos[0].cantidadMinima = Convert.ToDouble(txtCantMinima.Text.Trim());

                productosTa.Update(centerQuimDs.Productos);

                txtDescripcion.Text = "T " + txtDescripcion.Text.Trim().ToUpper(); //Se coloca el formato correcto para la descripción


                //Se muestra un mje de confirmación.
                MessageBox.Show("Producto editado con exito!!!", "AVISO", MessageBoxButtons.OK, MessageBoxIcon.Information);

                refrescarResgProductos = true;  //como se agregó un producto el resgProductos está desactualizado.

                resguardarProductoActual(); //Se resguarda el producto actual editado correctamente.

                estEsperandoOperacion(); //Luego de insertar un usuario se vuelve al estado esperando operación. Para que pueda editarlo o eliminarlo rapidamente.

            }
        }

        protected virtual void agregarProducto()
        {
            DialogResult result = MessageBox.Show("¿Está seguro en ingresar el nuevo producto?.", "CONFIRMACION INGRESO DE PRODUCTO", MessageBoxButtons.YesNo, MessageBoxIcon.Question);

            if (result == DialogResult.Yes)
            {

                //(ALE)FIJATE SI ESTÁ TODO BIEN. QUE COINCIDA CON TU CONSULTA
                productosTa.InsertQueryProducto(txtCodBarra.Text.Trim(), "T " + txtDescripcion.Text.Trim().ToUpper(), Math.Round(Convert.ToDecimal(txtPrecioCosto.Text.Trim()), 2), Math.Round(Convert.ToDecimal(txtPrecioVenta.Text.Trim()), 2), Convert.ToDecimal(txtStockActual.Text.Trim()), Convert.ToDecimal(txtCantMinima.Text.Trim()), "T", "", "", 0, 0, true, DateTime.Today);
                //se registra el movimiento del producto
                               

                //Se muestra un mje de confirmación.
                MessageBox.Show("Producto ingresado con exito!!!", "AVISO", MessageBoxButtons.OK, MessageBoxIcon.Information);

                refrescarResgProductos = true;  //como se agregó un producto el resgProductos está desactualizado.

                darFormatoADatos(); //Arregla los valores pasando a mayuscula, dejando como quedó en la base de datos.

                resguardarProductoActual(); //Se resguarda el producto actual insertado correctamente.

                estEsperandoOperacion(); //Luego de insertar un usuario se vuelve al estado esperando operación. Para que pueda editarlo o eliminarlo rapidamente.
            }

        }

        private void darFormatoADatos()
        {
            txtCodBarra.Text = txtCodBarra.Text.Trim();
            txtDescripcion.Text = "T " + txtDescripcion.Text.Trim().ToUpper();
            txtPrecioCosto.Text = Math.Round(Convert.ToDecimal(txtPrecioCosto.Text.Trim()), 2).ToString();
            txtPrecioVenta.Text = Math.Round(Convert.ToDecimal(txtPrecioVenta.Text.Trim()), 2).ToString();
            txtStockActual.Text = Convert.ToDecimal(txtStockActual.Text.Trim()).ToString();
            txtCantMinima.Text = Convert.ToDecimal(txtCantMinima.Text.Trim()).ToString();

        }

        protected virtual bool formularioValido()
        {
            
            if (estadoDelFormulario == "estAgregar")
            {
                validarCodBarra();

                validarDescripcion();

                validarPrecioCosto();

                validarPrecioVenta();

                validarStockActual();

                validarCantMinima();

            } else    //estEditar. Cuando se edita solo se controla estos valores
            {
                validarDescripcion();

                validarPrecioCosto();

                validarPrecioVenta();

                validarCantMinima();
            }
            


            if ((errorProvider1.GetError(txtCodBarra) == "") && (errorProvider1.GetError(txtDescripcion) == "") && (errorProvider1.GetError(txtPrecioCosto) == "") && (errorProvider1.GetError(txtPrecioVenta) == "") && (errorProvider1.GetError(txtStockActual) == "") && (errorProvider1.GetError(txtCantMinima) == ""))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        protected void validarCodBarra()
        {
            if (txtCodBarra.Text.Trim() == "")
            {
                txtCodBarra.Text = "0";  //Como no es obligatorio el código de barra puede ser vacio, en cuyo caso iría cero como código.
            }

            errorProvider1.SetError(txtCodBarra, new ValidarDatos().validarLongitud("Código de barra:", txtCodBarra.Text, 13) + new ValidarDatos().validarNaturales("Código de barra:", txtCodBarra.Text));
            
        }

        protected virtual void validarDescripcion()
        {
            if (txtDescripcion.Text.Trim() == "")
            {
                errorProvider1.SetError(txtDescripcion, "Descripcion: el campo se encuentra vacio\n\n");
            }
            else
            {
                if (this.estadoDelFormulario == "estAgregar")
                {
                    errorProvider1.SetError(txtDescripcion, new ValidarDatos().validarLongitud("Descripcion:", txtDescripcion.Text, 50) + validarDuplDescripcion(0));

                }
                else //estEditar
                {
                    if (("T " + txtDescripcion.Text.Trim().ToUpper()) == resgProductoActual[1]) //Si se va a editar y no cambia la descripción. Puede haber una sola coincidencia (el mismo producto)
                    {
                        errorProvider1.SetError(txtDescripcion, new ValidarDatos().validarLongitud("Descripcion:", txtDescripcion.Text, 50) + validarDuplDescripcion(1));
                    }
                    else
                    {
                        errorProvider1.SetError(txtDescripcion, new ValidarDatos().validarLongitud("Descripcion:", txtDescripcion.Text, 50) + validarDuplDescripcion(0)); //Si se va a editar y cambia la descripción. No puede haber coincidencias con la descripcion.
                    }

                }

            }
        }

        protected void validarPrecioCosto()
        {
            if (txtPrecioCosto.Text.Trim() == "")
            {
                errorProvider1.SetError(txtPrecioCosto, "Precio de Costo: el campo se encuentra vacio\n\n");
            }
            else
            {
                errorProvider1.SetError(txtPrecioCosto, new ValidarDatos().validarMoneda("Precio de Costo:", txtPrecioCosto.Text));
            }
        }


        protected void validarPrecioVenta()
        {
            if (txtPrecioVenta.Text.Trim() == "")
            {
                errorProvider1.SetError(txtPrecioVenta, "Precio de Venta: el campo se encuentra vacio\n\n");
            }
            else
            {
                errorProvider1.SetError(txtPrecioVenta, new ValidarDatos().validarMoneda("Precio de Venta:", txtPrecioVenta.Text));
            }
        }

        protected virtual void validarStockActual()  //el stock actual debe ser en este caso entero positivo con el cero. Ya que son productos terminados.
        {
            if (txtStockActual.Text.Trim() == "")
            {
                errorProvider1.SetError(txtStockActual, "Stock Actual: el campo se encuentra vacio\n\n");
            }
            else
            {
                errorProvider1.SetError(txtStockActual, new ValidarDatos().validarNaturales("Stock Actual:", txtStockActual.Text));
            }
        }

        protected virtual void validarCantMinima() //la cant.Minima debe ser en este caso entero positivo con el cero. Ya que son productos terminados.
        {
            if (txtCantMinima.Text.Trim() == "")
            {
                errorProvider1.SetError(txtCantMinima, "Cantidad Mínima: el campo se encuentra vacio\n\n");
            }
            else
            {
                errorProvider1.SetError(txtCantMinima, new ValidarDatos().validarNaturales("Cantidad Mínima:", txtCantMinima.Text));
            }
        }

        protected virtual string validarDuplDescripcion(int maxCoincidencia) //valida que no existan otros productos con esta descripción. El argumento define el maximo de coincidencia que puede aceptarse.
        {
            int coincidencia = 0;

            foreach (Producto unProducto in resgProductos) //buscamos las descripciones que coinciden con (una T + un caracter blanco + txtDescripcion.text) y la cargamos en el lstProductosDescripcion
            {
                if (Regex.Match(unProducto.Descripcion, @"^T\s" + txtDescripcion.Text.Trim().ToUpper() + "$").Success)
                {
                    coincidencia++;
                }

            }

            if (coincidencia > maxCoincidencia)
            {
                return "Descripcion: ya existe otro producto con ésta descripción.";
            }
            else
            {
                return "";
            }

        }

        
        protected void presionoEnter(object sender, System.Windows.Forms.KeyEventArgs e) //captura el evento KeyDown. Cuando se presiona la tecla Enter en los textBox PrecioVenta, precioCosto, StockActual, CantMin.
        {
            if (e.KeyCode == Keys.Enter)
            {
                procesar();
            }

        }

        protected void btnBusDescripcion_Click(object sender, EventArgs e) //captura el evento Click sobre el boton btnBusDescripcion
        {
            eventoBusDescripcion();
        }

        protected void lstProductosCodBarra_KeyDown(object sender, KeyEventArgs e) //captura el evento keyDown cuando se presiona enter sobre lstProductosCodBarra
        {
            if (e.KeyCode == Keys.Enter)
            {
                procesarEleccionLstProductosCodBarra();
            }else
            {
                if ((e.KeyCode == Keys.Up) & (lstProductosCodBarra.SelectedIndex == 0))//si llega al primer lugar y presiona para arriba, vuelve al txtCodBarra
                {
                    txtCodBarra.Focus();
                }
            }
        }

        protected void lstProductosDescripcion_KeyDown(object sender, KeyEventArgs e) //captura el evento keyDown cuando se presiona enter sobre lstProductosDescripcion
        {
            if (e.KeyCode == Keys.Enter)
            {
                procesarEleccionLstProductosDescripcion();
            }
            else
            {
                if ((e.KeyCode == Keys.Up) & (lstProductosDescripcion.SelectedIndex == 0)) //si llega al primer lugar y presiona para arriba, vuelve al txtDescripcion
                {
                    txtDescripcion.Focus();
                }
                
            }
        }

        private string obtenerFechaActual()  //Dato necesario para el alta de venta
        {
            frmPlantillaPadre unFrm = (frmPlantillaPadre)this.MdiParent;
            return unFrm.getFechaActual();
        }

        private void txtCodBarra_TextChanged(object sender, EventArgs e)  //si se modifica txtCodBarra, se oculta lstCodBarra
        {
            lstProductosCodBarra.Visible = false;
            lstProductosCodBarra.Enabled = false;
        }

        protected virtual void frmGestionProductos_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F1)
            {

                bool tieneError = false;

                foreach (System.Windows.Forms.Control ctrl in this.Controls)
                {
                    if (errorProvider1.GetError(ctrl) != "")
                    {
                        tieneError = true;
                    }
                }

                if (tieneError) mostrarErrores();
            }
            else
            {
                if (e.KeyCode == Keys.F12)
                {
                    refrescarResgProductos = true;

                }
            }
        }


    }
}